
/**
 *
 * @author group_0217
 */
import java.net.*;
import java.io.*;
import java.util.HashMap;

public class Commands {
    // This class contains all the commands that the user can use, 
    // decide what to do according to what the users type in.

    /**
     * 
     * @param path the path to be checked if full.
     * @return true if path is full path. Otherwise, return false.
     */
    private Boolean isFull(String path) {
        String[] pa = path.split("/");
        // Return if the first item is "JShell".
        return pa.length > 0 && pa[0].equals("JShell");
    }

    /**
     * Trace the given path in a JShell object.
     * @param js the root Directory.
     * @param fpath the path of target Directory
     * @return the Directory if a Directory object exists under the 
     * given path.
     * @throws NoDirectoryError if no Directory object exists under
     * the given path.
     * @throws NotDirectoryError if a file under the given path.
     */
    private Directory travel(JShell js, String fpath) throws NoDirectoryError, NotDirectoryError {
        if (!isFull(fpath)) {
            // Make it a full path first.
            fpath = js.getCurLocation() + "/" + fpath;
        }
        // Split the full path.
        String[] pa = fpath.split("/");
        Directory dir = js.getJs();
        for (int i = 0; i < pa.length; i++) {
            String sig = pa[i];
            if (!dir.getDir().containsKey(sig)) {
                // If no such a Directory.
                throw new NoDirectoryError(sig + ": No Such File or Directory");
            } else if (!(dir.getDir().get(sig) instanceof Directory)) {
                // If it is a File instead of a Directory.
                throw new NotDirectoryError(sig + ": Not a Directory");
            } else {
                // We trace inside.
                dir = (Directory) dir.getDir().get(sig);
            }
        }
        return dir;
    }

    /**
     * If the given path eligible, set the current location of JShell.
     * @param js the root Directory.
     * @param com the path of target working Directory.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String cd(JShell js, String[] com) {
        try {
            String fpath;
            if (com.length > 0 && !com[0].equals("/")) {
                // If path is given.
                fpath = com[0];
            } else {
                // If path not given.
                fpath = "JShell";
            }
            // Check if the path is eligible.
            travel(js, fpath);
            if (!isFull(fpath)) {
                // Make it a full path.
                fpath = js.getCurLocation() + "/" + fpath;
            }
            // Change the current working path.
            js.setCurLocation(fpath);
            return "good";

        } catch (Exception e) {
            // If Exception happens, like if the path is not eligible.
            return e.getMessage();
        }
    }

    /**
     * Create a Directory in the given path(if eligible) in the JShell.
     * @param js the root Directory.
     * @param com the path array of the Directory to be created.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String makeDir(JShell js, String[] com) {
        try {
            if (com.length < 1) {
                // If path not given.
                throw new CommandUSingError("usage: mkdir DIR ...");
            }
            // For each given path.
            for (String path : com) {
                // Get the path of the parent Directory and the name of 
                // the Directory to be created.
                String[] fr = cutPath(path);
                // Get the parent Directory.
                Directory di = (Directory) travel(js, fr[0]);
                // Create a new Directory in the parent Directory.
                di.createDirectory(fr[1]);
            }
            return "good";
        } catch (Exception e) {
            // If Exception happens, like if the path not eligible.
            return e.getMessage();
        }
    }

    /**
     * 
     * @param path the path to be cut.
     * @return a String array, which is of length two. The second contains
     * the last term of the path and the first contains the rest. For example,
     * Split "JShell/Sammy/a" into "JShell/Sammy" and "s".
     */
    private String[] cutPath(String path) {

        String[] fr = path.split("/");
        // String array of size two.
        String[] newPath = new String[2];
        newPath[0] = "";
        // The last term.
        newPath[1] = fr[fr.length - 1];

        for (int i = 0; i < fr.length - 1; i++) {
            // Get the rest.
            newPath[0] += fr[i];
            if (i != fr.length - 2) {
                newPath[0] += "/";
            }
        }
        return newPath;
    }

    /**
     * Open the File under the given path(if eligible) in the JShell.
     * @param js the root Directory.
     * @param com the path of the File to be opened.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String catFile(JShell js, String[] com) {
        // Display the contents of the file of the given path.

        try {
            if (com.length < 1) {
                // If path not given.
                throw new CommandUSingError("usage: cat FILE");
            }
            String path = "";
            if (com.length > 0) {
                path = com[0];
            }
            // Get the path of the parent Directory.
            String[] fr = cutPath(path);
            // Get the parent Directory.
            Directory di = (Directory) travel(js, fr[0]);
            if (!di.ifContain(fr[1])) {
                // If File not exists.
                throw new NoDirectoryError(path + ": No Such File or Directory");
            }
            if (di.getDir().get(fr[1]) instanceof Link) {
                // If the given path is a Link.
                String[] command = {((Link) di.getDir().get(fr[1])).getLn()};
                return catFile(js, command);
            } else {
                di.openFile(fr[1]);
            }
            return "good";

        } catch (Exception e) {
            // If Exception happens, like if the path not eligible or 
            // the File not exists.
            return e.getMessage();
        }
    }

    /**
     * Append the given contents to the file or overwrite the contents of the 
     * file of the given path. If the file of the given path doesn't 
     * exist, create one with the given contents.
     * @param js the root Directory.
     * @param contents the contents used to append or overwrite.
     * @param com the path of the File to be operated or created.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String echo(JShell js, String[] Com) {

        try {
            String[] com = echoHelper(Com);
            String contents = com[0];
            if (com[1].equals("")) {
                return com[0];
            }
            // Get the path of the parent Directory.
            String[] fr = cutPath(com[2]);
            // Get the parent Directory.
            Directory di = (Directory) travel(js, fr[0]);
            // Append the String to the File.
            if (com[1].equals(">>")) {
                // If ">>" is given.
                di.appendFile(fr[1], contents);
            } else {
                // If ">" is given.
                di.overwriteFile(fr[1], contents);
            }
            return "good";

        } catch (Exception e) {
            // If Exception happens, like if the path not eligible or 
            // the File not exists.
            return e.getMessage();
        }
    }

    /**
     * Split the contents, ">>" or ">" sign, and the path.
     * @param Com the command array to be splited.
     * @return a String array, where com[0] is the contents, com[1] is ">>" or ">", and
     * com[2] is the path.
     */
    private String[] echoHelper(String[] Com) {

        String[] com = {"", "", ""};
        for (int i = 0; i < Com.length; i++) {
            if (Com[i].equals(">>") || Com[i].equals(">")) {
                // When we meet ">>" or ">", the next element is the path.
                com[1] = Com[i];
                com[2] = Com[i + 1];
                break;
            } else {
                // Get the contents.
                com[0] = com[0] + Com[i] + ' ';
            }
        }
        return com;
    }

    /**
     * Print the names of the Files and Directories under
     * the Directory of the given path.
     * @param js the root Directory.
     * @param com the path array of the Directory to be listed.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String ls(JShell js, String[] com) {
        try {
            Object[] newCom = lsHelper(com);
            for (String path : (String[]) newCom[1]) {
                // For all the given paths.
                try {
                    // Get the Directory to be listed.
                    Directory di = travel(js, path);
                    if (!isFull(path)) {
                        // Make it a full path.
                        if (!path.equals("")) {
                            path = js.getCurLocation() + '/' + path;
                        } else {
                            path = js.getCurLocation();
                        }
                    }
                    System.out.println(path + ": ");
                    // Display the contents.
                    di.list();
                    if ((Boolean) newCom[0]) {
                        // If "-R" is given, we recursively display the contents.
                        lsRecur(js, di, path);
                    }
                } catch (Exception e) {
                    if (e instanceof NotDirectoryError) {
                        // The given path is a File, then we get the File.
                        String[] parentChild = cutPath(path);
                        Directory di = travel(js, parentChild[0]);
                        if (!isFull(path)) {
                            // Make it a full path.
                            if (!path.equals("")) {
                                path = js.getCurLocation() + '/' + path;
                            } else {
                                path = js.getCurLocation();
                            }
                        }
                        System.out.println(path + ": ");
                        // Display the File name.
                        di.getDir().get(parentChild[1]).list();
                    } else {
                        System.out.println(e.getMessage());
                    }
                }
            }
            return "good";
        } catch (Exception e) {
            // If Exception happens, like if the path not eligible.
            return e.getMessage();
        }
    }

    /**
     * Split "-R" and the paths.
     * @param com the command to be splited.
     * @return an Object array, the first element is true if "-R" is given.
     * oherwiss, false. The second element is a String array, which contains
     * the paths.
     */
    private Object[] lsHelper(String[] com) {
        Object[] newCom = new Object[2];
        int i;
        int j = 0;
        if (com.length == 0) {
            // "-R" not given and go back to the list the root Directory.
            newCom[0] = false;
            newCom[1] = new String[1];
            ((String[]) newCom[1])[0] = "";
            return newCom;
        } else if (com[0].equals("-R")) {
            // "-R" is given.
            newCom[0] = true;
            if (com.length == 1) {
                newCom[1] = new String[1];
                ((String[]) newCom[1])[0] = "";
            } else {
                newCom[1] = new String[com.length - 1];
            }
            i = 1;
        } else {
            // "-R" not given.
            newCom[0] = false;
            newCom[1] = new String[com.length];
            i = 0;
        }
        while (i < com.length) {
            // Get the exact String array.
            ((String[]) newCom[1])[j] = com[i];
            i++;
            j++;
        }
        return newCom;
    }

    /**
     * Recursively display the contents of the given Directory.
     * @param js the root Directory.
     * @param di the Directory to be recursively listed.
     * @param path the path of the parent Directory, inorder to make the full path.
     */
    private void lsRecur(JShell js, Directory di, String path) {
        // Get all the contents in a array.
        Object[] toList = di.getDir().keySet().toArray();
        // The number of Data to be listed, doesn't include "." and "..".
        int size = 0;
        for (int i = 0; i < toList.length; i++) {
            if (!((String) toList[i]).equals(".") && !((String) toList[i]).equals("..")) {
                size++;
            }
        }
        if (size != 0) {
            // If size == 0, nothing needs to do.
            String[] RecurCom = new String[size + 1];
            RecurCom[0] = "-R";
            int k = 1;
            int j = 0;
            while (k < RecurCom.length) {
                if (!((String) toList[j]).equals(".") && !((String) toList[j]).equals("..")) {
                    RecurCom[k] = path + '/' + (String) toList[j];
                    k++;
                    j++;
                } else {
                    j++;
                }
            }
            ls(js, RecurCom);
        }
    }

    /**
     * copy a object from oldpath to new path, if be directory, recursivly copy all items inside
     * @param js, the JShell object where we were working
     * @param path, a 2 strings list, each might be full path or relative path, firth be oldpath,
     * second be new path
     * @return "good" if no exception raises, return Error message otherwise
     * @throws NoDirectoryError, thrown when do not contain such directory
     * @throws NotDirectoryError, thrown when travel to non-directory object
     */
    public String cp(JShell js, String[] path) throws NoDirectoryError, NotDirectoryError {
        //use try and catch to control the object type relative to path, and take a copy of 
        //oldpath object and write to newpath.
        if (path.length < 2) {
            return "usage: cp OLDPATH NEWPATH";
        }
        String[] oldy = cutPath(path[0]);
        String[] newy = cutPath(path[1]);
        try {
            Directory dirOld = travel(js, path[0]);
            String name = dirOld.returnName();
            try {
                //to directory
                Directory dirNew = travel(js, path[1]);
                if (!dirNew.ifContain(name)) {
                    //if exits overwrite
                    dirNew.getDir().put(name, dirOld.copy());
                    dirNew.getDir().get(name).setParent(dirNew);
                } else {
                    if (dirNew.getDir().get(name) instanceof Directory) {
                        // if new be Directory copy all thing in, overwrite if exits item with same name 
                        // and data type
                        dirNew.getDir().put(name, dirOld.copy());
                        dirNew.getDir().get(name).setParent(dirNew);
                    } else {
                        return "cannot overwrite file with directory";
                    }
                }
                return "good";
            } catch (Exception e) {
                return "cannot copy to file";
            }
        } catch (Exception e) {
            Directory dirOld = travel(js, oldy[0]);
            String name = oldy[1];
            //copy and overwrite files
            try {
                Directory dirNew = travel(js, path[1]);
                Data top = dirOld.getDir().get(name);
                if (!dirNew.ifContain(name)) {
                    dirNew.getDir().put(name, top.copy());
                    dirNew.getDir().get(name).setParent(dirNew);
                } else {
                    if (dirNew.getDir().get(name) instanceof File) {
                        dirNew.getDir().put(name, top.copy());
                        dirNew.getDir().get(name).setParent(dirNew);
                    } else {
                        return "cannot overwrite directory with file";
                    }
                }
                return "good";
            } catch (Exception e1) {
                //over write file if copy from file to file
                Directory dirNew = travel(js, newy[0]);
                String newName = newy[1];
                Data top = dirOld.getDir().get(name);
                dirNew.getDir().put(newName, top.copy());
                return "good";
            }
        }
    }

    /**
     * move a object from oldpath to new path, if be directory, recursivly move all items inside
     * @param js, the JShell object where we were working
     * @param path, a 2 strings list, each might be full path or relative path, firth be oldpath,
     * second be new path
     * @return "good" if no exception raises, return Error message otherwise
     * @throws NoDirectoryError, thrown when do not contain such directory
     * @throws NotDirectoryError, thrown when travel to non-directory object
     */
    public String mv(JShell js, String[] path) throws NoDirectoryError, NotDirectoryError {
        //use try and catch to control the object type relative to path, and move 
        //oldpath object to newpath.
        if (path.length < 2) {
            return "usage: mv OLDPATH NEWPATH";
        }
        String[] oldy = cutPath(path[0]);
        String[] newy = cutPath(path[1]);
        try {
            Directory dirOld = travel(js, path[0]);
            Directory prOld = dirOld.parent;
            String name = dirOld.returnName();
            prOld.remove(name);
            try {
                Directory dirNew = travel(js, path[1]);
                if (!dirNew.ifContain(name)) {
                    dirNew.getDir().put(name, dirOld);
                    dirNew.getDir().get(name).setParent(dirNew);
                } else {
                    //same as move
                    if (dirNew.getDir().get(name) instanceof Directory) {
                        dirNew.getDir().put(name, dirOld);
                        dirNew.getDir().get(name).setParent(dirNew);
                    } else {
                        return "cannot overwrite file with directory";
                    }
                }
                return "good";
            } catch (Exception e) {
                return "cannot overwrite file with directory";
            }
        } catch (Exception e) {
            Directory dirOld = travel(js, oldy[0]);
            String name = oldy[1];
            try {
                Directory dirNew = travel(js, path[1]);
                Data top = dirOld.getDir().get(name);
                dirOld.remove(name);
                if (!dirNew.ifContain(name)) {
                    //same as move 
                    dirNew.getDir().put(name, top);
                    dirNew.getDir().get(name).setParent(dirNew);
                } else {
                    if (dirNew.getDir().get(name) instanceof File) {
                        dirNew.getDir().put(name, top);
                        dirNew.getDir().get(name).setParent(dirNew);
                    } else {
                        return "cannot overwrite directory with file";
                    }
                }
                return "good";
            } catch (Exception e1) {
                Directory dirNew = travel(js, newy[0]);
                String newName = newy[1];
                Data top = dirOld.getDir().get(name);
                dirOld.remove(name);
                dirNew.getDir().put(newName, top);
                return "good";
            }
        }
    }

    /**
     * 
     * @param js the root Directory.
     * @param com the command array.
     * @return the current working path.
     */
    public String pwd(JShell js, String[] com) {
        return js.getCurLocation();
    }

    /**
     * Make a link between two paths, the target path has to be a File.
     * @param js the root Directory.
     * @param com the String array contains Path1 and Path2.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String ln(JShell js, String[] com) {
        try {
            if (com.length < 2) {
                // We need two paths.
                throw new CommandUSingError("usage: ln PATH1 PATH2");
            }
            String path1, path2;
            // Get the two paths.
            path1 = com[0];
            path2 = com[1];
            String[] P1 = cutPath(path1);
            String[] P2 = cutPath(path2);
            Directory D1 = travel(js, P1[0]);
            Directory D2 = travel(js, P2[0]);
            if (!D2.getDir().containsKey(P2[1])) {
                // If path2 not exists.
                throw new FileExistError(P2[1] + ": No such a file or directory");
            } else if (D2.getDir().get(P2[1]) instanceof Directory) {
                // If path2 is a Directory.
                throw new NotFileError(P2[1] + ": hard link not allowed for directory");
            } else if (D1.ifContain(P1[1])) {
                // If path1 already exists.
                throw new FileExistError(P1[1] + ": File exists");
            }
            if (!isFull(path2)) {
                // Make it a full path.
                path2 = js.getCurLocation() + '/' + path2;
            }
            // Create the Link object.
            D1.getDir().put(P1[1], new Link(path2, P1[1]));
            return "good";
        } catch (Exception e) {
            // If error happens.
            return e.getMessage();
        }
    }

    /**
     * Recursively search PATH (which must be a Directory)for file 
     * names containing REGEX and print them (including their 
     * path), one per line.
     * @param js the root Directory.
     * @param regex the String to be searched.
     * @param path the path of the directory to be searched.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String find(JShell js, String[] com) {
        try {
            if (com.length < 2) {
                // We need the REGEX and the path.
                throw new CommandUSingError("usage: find REGEX PATH ...");
            }
            String regex = com[0];
            for (int k = 1; k < com.length; k++) {
                // For all paths.
                String path = com[k];
                Directory di = travel(js, path);
                Object[] Contents = di.getDir().keySet().toArray();
                for (Object i : Contents) {
                    // Search the contents.
                    if (((String) i).contains(regex)) {
                        if (!i.equals(".") && !i.equals("..")) {
                            System.out.println(path + '/' + i);
                        }
                    }
                }
                for (Object i : Contents) {
                    // If any of the contents is Directory, recursively search REGEX in it.
                    String j = (String) i;
                    if (di.getDir().get(j) instanceof Directory && (!j.equals(".")) && (!j.equals(".."))) {
                        String[] recurPath = new String[2];
                        recurPath[0] = regex;
                        recurPath[1] = path + '/' + i;
                        find(js, recurPath);
                    }
                }
            }
            return "good";
        } catch (Exception e) {
            // If error happens.
            return e.getMessage();
        }
    }

    /**
     * Print any lines containing REGEX in PATH. If PATH is Directory 
     * and "-R" is given, recursively search the PATH.
     * @param js the root Directory.
     * @param com contains the content to be searched and the target path(and "-R").
     * @return "good" if no error happens, else return the message of the error.
     */
    public String grep(JShell js, String[] com) {
        try {
            Object[] newCom = grepHelper(com);
            String regex = ((String[]) newCom[1])[0];
            Boolean R = (Boolean) newCom[0];
            for (int i = 1; i < ((String[]) newCom[1]).length; i++) {
                // For all the PATHs.
                String[] pathName = cutPath(((String[]) newCom[1])[i]);
                Directory di = travel(js, pathName[0]);
                if (!di.ifContain(pathName[1])) {
                    // If path doesn't exist.
                    throw new NoDirectoryError(((String[]) newCom[1])[i] + ": No Such File or Directory");
                }
                Data data = di.getDir().get(pathName[1]);
                if (data instanceof Directory & !R) // If "-R" not given and PATH is a Directory.
                {
                    throw new NotFileError(((String[]) newCom[1])[i] + ": Is a Directory");
                }
                if (data instanceof File) {
                    System.out.println(((String[]) newCom[1])[i] + ":\n" + data.grep(regex));
                } else if (data instanceof Link) {
                    // If data is a Link, search the linked File.
                    String[] comLink = new String[2];
                    comLink[0] = regex;
                    comLink[1] = ((Link) data).getLn();
                    grep(js, comLink);
                } else if (data instanceof Directory && R) {
                    // If data Directory and "-R" given, we recursively search it.
                    Directory dir = (Directory) data;
                    Object[] contents = dir.getDir().keySet().toArray();
                    int length = 0;
                    for (int j = 0; j < contents.length; j++) {
                        if (!contents[j].equals("..") && !contents[j].equals(".")) {
                            length++;
                        }
                    }
                    String[] comDir = new String[length + 2];
                    comDir[0] = "-R";
                    comDir[1] = regex;
                    int k = 2;
                    int l = 0;
                    while (k < comDir.length) {
                        if (!contents[l].equals("..") && !contents[l].equals(".")) {
                            comDir[k] = ((String[]) newCom[1])[i] + "/" + (String) contents[l];
                            k++;
                        }
                        l++;
                    }
                    grep(js, comDir);
                }
            }
            return "good";
        } catch (Exception e) {
            // If error happens.
            return e.getMessage();
        }
    }

    /**
     * Split "-R" and PATHs.
     * @param com the command array to be operated.
     * @return a Object array. The first element is true is "-R" given, otherwise false.
     * The second element contains PATHs.
     * @throws CommandUSingError if the command is not properly used.
     */
    private Object[] grepHelper(String[] com) throws CommandUSingError {
        Object[] newCom = new Object[2];
        int size;
        int startPoint;
        if (com.length < 2) {
            // At least we need REGEX and PATH.
            throw new CommandUSingError("usage: grep [-R] REGEX PATH ...");
        }
        if (com[0].equals("-R")) {
            // If "-R" given.
            newCom[0] = true;
            size = com.length - 1;
            startPoint = 1;
        } else {
            // If "-R" not given.
            newCom[0] = false;
            size = com.length;
            startPoint = 0;
        }
        if (size < 2) {
            // At least we need REGEX and PATH.
            throw new CommandUSingError("usage: grep [-R] REGEX PATH ...");
        }
        newCom[1] = new String[size];
        int j = 0;
        int i = startPoint;
        while (i < com.length) {
            ((String[]) newCom[1])[j] = com[i];
            i++;
            j++;
        }

        return newCom;
    }

    /**
     * read command of rm break down and return an length-2-object Array, 
     * first be a boolean statement representing -f's appearance, 
     * second index be a String Array, being the rest commands
     * @param com, a list of string, the product of commands splited by " ", i.e. space
     * @return an object String, first index be boolean, followed by a list of String, 
     * which is the remain part of command
     * @throws CommandUSingError, thrown when not be a valid command
     */
    private Object[] rmHelper(String[] com) throws CommandUSingError {
        Object[] newCom = new Object[2];
        int i;
        int j = 0;
        if (com.length < 1) {
            throw new CommandUSingError("usage: rm [-f] PATH ...");
        }
        if (com[0].equals("-f")) {
            // if -f appears, sign truth
            newCom[0] = true;
            i = 1;
            newCom[1] = new String[com.length - 1];
            while (i < com.length) {
                ((String[]) newCom[1])[j] = com[i];
                i++;
                j++;
            }
        } else {
            //else, i.e. -f not in
            newCom[0] = false;
            i = 0;
            newCom[1] = new String[com.length];
            while (i < com.length) {
                ((String[]) newCom[1])[j] = com[i];
                i++;
                j++;
            }
        }
        return newCom;
    }

    /**
     * main body of remove command, called in JShell
     * @param js, JShell object where we were working
     * @param com, the product of command splited by space
     * @return the result of rmF or rm or the message of Error that happens
     */
    public String rmMain(JShell js, String[] com) {
        try {
            Object[] newCom = rmHelper(com);
            if ((Boolean) newCom[0]) {
                // -f in, use rmF method 
                return rmF(js, (String[]) newCom[1]);
            } else {
                // -f not in use rm method
                return rm(js, (String[]) newCom[1]);
            }
        } catch (Exception e) {
            // If Exception happens, like if the path not eligible.
            return e.getMessage();
        }
    }

    /**
     * remove command if -f not appears. not need to confirm.
     * @param js, JShell objcet where we were working
     * @param sigPath, a list of path, might be full path or relative path
     * @return good or message of Error that happens
     */
    private String rmF(JShell js, String[] sigPath) {
        for (String path : sigPath) {
            //deal with path list singlely
            String[] cuty = cutPath(path);
            try {
                //if directory
                Directory dir = travel(js, path);
                dir = travel(js, cuty[0]);
                dir.remove(cuty[1]);
            } catch (Exception e) {
                try {
                    //if not directory
                    Directory dir = travel(js, cuty[0]);
                    dir.remove(cuty[1]);
                } catch (Exception e1) {
                    //error happens, return message
                    return e.getMessage();
                }
            }
        }
        return "good";
    }

    /**
     * remove method when -f appears, remove if item comfirmed.
     * @param js, JShell object where we were working
     * @param sigPath, a list of string, each might be full path or relative path
     * @return return good if achieved, or return message of Error that happens
     */
    private String rm(JShell js, String[] sigPath) {
        for (String path : sigPath) {
            //can handle with list of path
            String[] cuty = cutPath(path);
            try {
                Directory dir = travel(js, path);
                if (!isFull(path)) {
                    path = js.getCurLocation() + "/" + path;
                }
                //if remove directory, use help function, which is recursive
                rmHelp(dir, path);
                System.out.println("Do you really want to delete " + path + " ?  Y/N");
                BufferedReader dc = new BufferedReader(new InputStreamReader(System.in));
                String cfirm = dc.readLine();
                if (cfirm.equals("Y")) {
                    if (dir.parent.getDir().keySet().toArray().length <= 2) {
                        dir.parent.remove(dir.returnName());
                    }
                }
            } catch (Exception e) {
                try {
                    if (e instanceof NoDirectoryError) {
                        //error
                        return e.getMessage();
                    }
                    //remove file
                    Directory dir = travel(js, cuty[0]);
                    System.out.println("Do you really want to delete " + path + " ?  Y/N");
                    BufferedReader dc = new BufferedReader(new InputStreamReader(System.in));
                    String cfirm = dc.readLine();
                    if (cfirm.equals("Y")) {
                        dir.remove(cuty[1]);
                    }
                } catch (Exception e1) {
                    return e.getMessage();
                }
            }
        }
        return "good";
    }

    /**
     * helper function of recursively function, used when need to remove directory and its contents
     * @param dir, the directory currently working on
     * @param cuPath, the full path of currently worked directory
     */
    private void rmHelp(Directory dir, String cuPath) {
        Object[] keys = dir.getDir().keySet().toArray();
        for (Object okey : keys) {
            String key = (String) okey;
            if (key.equals(".") || key.equals("..")) {
            } else {
                if (dir.getDir().get(key) instanceof Directory) {
                    //recursive part, goes to the deepest and return back
                    rmHelp((Directory) dir.getDir().get(key), cuPath + "/" + key);
                    System.out.println("Do you really want to delete" + cuPath + "/" + key + " ?  Y/N");
                    BufferedReader dc = new BufferedReader(new InputStreamReader(System.in));
                    try {
                        String cfirm = dc.readLine();
                        if (cfirm.equals("Y")) {
                            if (keys.length <= 2) {
                                dir.remove(key);
                            }
                        }
                    } catch (Exception e) {
                    }
                } else {
                    //meet file ask and remove
                    System.out.println("Do you really want to delete" + cuPath + "/" + key + " ?  Y/N");
                    BufferedReader dc = new BufferedReader(new InputStreamReader(System.in));
                    try {
                        String cfirm = dc.readLine();
                        if (cfirm.equals("Y")) {
                            dir.remove(key);
                        }
                    } catch (Exception e) {
                    }
                }
            }
        }
    }

    /**
     * Retrieve the file at that URL and add it to the
     * current directory.
     * @param js the root Directory.
     * @param com contains the URL to be retrieved.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String getURL(JShell js, String[] com) {
        try {
            if (com.length < 1) {
                // We need the URL.
                throw new CommandUSingError("usage: get URL(http://www. ...)");
            }
            if (!com[0].contains("http://www.")) {
                // If URL not eligible.
                throw new CommandUSingError("usage: get URL(http://www. ...)");
            }
            // Get the URL.
            URL toRead = new URL(com[0]);
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(toRead.openStream()));
            String inputLine;
            String path = com[0].replace('/', '$');
            // Write in the File.
            FileWriter fstream = new FileWriter(path + ".txt");
            BufferedWriter out = new BufferedWriter(fstream);
            while ((inputLine = in.readLine()) != null) {
                out.write(inputLine);
                System.out.println(inputLine);
            }
            out.close();
            in.close();
            return "good";
        } catch (Exception e) {
            if (e instanceof UnknownHostException) {
                // If host unknown.
                return "Unknown host: " + e.getMessage();
            } else {
                // If other error happens.
                return e.getMessage();
            }
        }
    }

    /**
     * Exit the program.
     * @param js the root Directory.
     * @param com the command array.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String exit(JShell js, String[] com) {
        return "good";
    }

    /**
     * Print documentation for CMD.
     * @param js the root Directory.
     * @param com the entry of the manual.
     * @return "good" if no error happens, else return the message of the error.
     */
    public String man(JShell js, String[] com) {
        try {
            if (com.length < 1) {
                // We need the entry.
                throw new CommandUSingError("What manual page do you want?");
            }
            HashMap<String, String> cr = new HashMap<String, String>();
            // A hashmap from the command to the docutation.
            cr.put("mkdir", "mkdirMAN.txt");
            cr.put("cd", "cdMAN.txt");
            cr.put("ls", "lsMAN.txt");
            cr.put("cp", "cpMAN.txt");
            cr.put("mv", "mvMAN.txt");
            cr.put("pwd", "pwdMAN.txt");
            cr.put("cat", "catMAN.txt");
            cr.put("echo", "echoMAN.txt");
            cr.put("ln", "lnMAN.txt");
            cr.put("find", "findMAN.txt");
            cr.put("rm", "rmMAN.txt");
            cr.put("grep", "grepMAN.txt");
            cr.put("get", "getMAN.txt");
            cr.put("exit", "exitMAN.txt");
            cr.put("man", "manMAN.txt");
            if (!cr.containsKey(com[0])) {
                // If no such entry.
                throw new CommandUSingError("No manual entry for " + com[0]);
            }
            FileInputStream fstream = new FileInputStream("src/" + cr.get(com[0]));
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String strLine;
            while ((strLine = br.readLine()) != null) {
                // Print the documentation.
                System.out.println(strLine);
            }
            in.close();
            return "good";
        } catch (Exception e) {
            // If error happens.
            return e.getMessage();
        }
    }
}
